package com.sf.shiva.oms.common.util;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sf.shiva.oms.common.exception.OmsRuntimeException;

/**
 * 
 * 描述：实体的复制工具类
 * 
 * <pre>
 * HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2016年12月20日         835897         Create
 * ****************************************************************************
 * </pre>
 * 
 * @author 835897
 * @version 1.0
 */
public class BeanCopyUtil extends BeanUtilsBean {

    private static final Logger logger = LoggerFactory.getLogger(BeanCopyUtil.class);
    private static BeanCopyUtil instance = new BeanCopyUtil();

    /**
     * 
     * @param dest
     *            目标实体对象
     * @param orig
     *            源实体对象
     * @param ignoreNullFlag
     *            是否忽略null值
     * @param ignoreProperties
     *            忽略的属性集合，不需要复制的属性
     */
    public static void copyProperties(Object dest, Object orig, boolean ignoreNullFlag,
            String... ignoreProperties) {
        try {
            BeanCopyUtil.getBeanInstance().copyPropertiesImpl(dest, orig, ignoreNullFlag,
                ignoreProperties);
        } catch (Exception e) {
            throw new OmsRuntimeException(e);
        }
    }

    public static BeanCopyUtil getBeanInstance() {
        return instance;
    }

    /**
     * 
     * @param dest
     *            目标对象
     * @param orig
     *            源对象
     * @param ignoreNullFlag
     *            是否忽略null值
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    @SuppressWarnings("rawtypes")
    private void copyPropertiesImpl(Object dest, Object orig, boolean ignoreNullFlag,
            String... ignoreProperties) throws IllegalAccessException, InvocationTargetException {
        if (dest == null) {
            throw new IllegalArgumentException("No destination bean specified");
        }
        if (orig == null) {
            throw new IllegalArgumentException("No origin bean specified");
        }
        List ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
        if (orig instanceof DynaBean) {
            DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties();
            for (int i = 0; i < origDescriptors.length; i++) {
                String name = origDescriptors[i].getName();
                copyDynaBeanProperties(dest, orig, ignoreNullFlag, ignoreList, name);
            }
        } else if (orig instanceof Map) {
            @SuppressWarnings("unchecked")
            // Map properties are always of type <String, Object>
            Map<String, Object> propMap = (Map<String, Object>) orig;
            for (Map.Entry<String, Object> entry : propMap.entrySet()) {
                String name = entry.getKey();
                Object value = entry.getValue();
                copyMapProperties(dest, ignoreNullFlag, ignoreList, name, value);
            }
        } else {/* if (orig is a standard JavaBean) */
            PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig);
            for (int i = 0; i < origDescriptors.length; i++) {
                String name = origDescriptors[i].getName();
                if ("class".equals(name)) {
                    continue; // No point in trying to set an object's class
                }
                if (getPropertyUtils().isReadable(orig, name)
                        && getPropertyUtils().isWriteable(dest, name)) {
                    copyStdBeanProperties(dest, orig, ignoreNullFlag, ignoreList, name);
                }
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private void copyStdBeanProperties(Object dest, Object orig, boolean ignoreNullFlag, List ignoreList, String name)
            throws IllegalAccessException, InvocationTargetException {
        try {
            Object value = getPropertyUtils().getSimpleProperty(orig, name);
            if (ignoreNullFlag) {
                if (value != null
                        && (((ignoreList == null) || (!(ignoreList.contains(name)))))) {
                    copyProperty(dest, name, value);
                }
            } else {
                if(((ignoreList == null) || (!(ignoreList.contains(name))))){
                    copyProperty(dest, name, value);
                }
            }
        } catch (NoSuchMethodException e) {
            logger.error("BeanUtils.copyProperties(" + dest + ", " + orig + ", Property:"
                    + name + ") error;"+ExceptionUtils.extractStackTrace(e));
        }
    }

    @SuppressWarnings("rawtypes")
    private void copyMapProperties(Object dest, boolean ignoreNullFlag, List ignoreList, String name, Object value)
            throws IllegalAccessException, InvocationTargetException {
        if (getPropertyUtils().isWriteable(dest, name)) {
            if (ignoreNullFlag) {
                if (value != null
                        && (((ignoreList == null) || (!(ignoreList.contains(name)))))) {
                    copyProperty(dest, name, value);
                }
            } else {
                if(((ignoreList == null) || (!(ignoreList.contains(name))))){
                    copyProperty(dest, name, value);
                }
            }
        }
    }

    @SuppressWarnings("rawtypes")
    private void copyDynaBeanProperties(Object dest, Object orig, boolean ignoreNullFlag, List ignoreList, String name)
            throws IllegalAccessException, InvocationTargetException {
        if (getPropertyUtils().isReadable(orig, name)
                && getPropertyUtils().isWriteable(dest, name)) {
            Object value = ((DynaBean) orig).get(name);
            if (ignoreNullFlag) {
                if (value != null
                        && (((ignoreList == null) || (!(ignoreList.contains(name)))))) {
                    copyProperty(dest, name, value);
                }
            } else {
                if(((ignoreList == null) || (!(ignoreList.contains(name))))){
                    copyProperty(dest, name, value);
                }
            }
        }
    }

}
